#!/usr/bin/env python
# -*- coding: utf-8 -*-

import xlrd
import argparse
import time
import re
import os
import sys
import math

__DESCRIPTION__ = '''
ASIC芯片中大部分功能模块都有寄存器，以配置模块功能和获取模块状态。硬件工程师和验证工程师都需要与寄存器打交道，如果有一份中心化且可读性强的寄存器描述文件，并以此生成寄存器硬件模块和寄存器验证模型，可降低寄存器硬件代码编制和验证时间，同时自动化的生成方式也避免出错。
本脚本输入为中心化的寄存器描述文件(Excel格式)，生成寄存器硬件模块、ralf模型。主要有以下特性:
1. APB接口
2. 地址和数据位宽可配
3. 可生成verilog代码和ralf模型
4. 支持25种寄存器存取类型
 1) RO      读写此域都无影响 
 2) RW      会尽量写入，读取时对此域无影响 
 3) RC      写入时无影响，读取时会清零 
 4) RS      写入时无影响，读取时会设置所有的位 
 5) WRC     尽量写入，读取时会清零 
 6) WRS     尽量写入，读取时会设置所有的位 
 7) WC      写入时会清零，读取时无影响 
 8) WS      写入时会设置所有的位，读取时无影响 
 9) WSRC    写入时会设置所有的位，读取时会清零 
10) WCRS    写入时会清零，读取时会设置所有的位 
11) W1C     写1清零，写0时无影响，读取时无影响 
12) W1S     写1设置所有的位，写0时无影响，读取时无影响 
13) W1T     写1入时会翻转，写0时无影响，读取时无影响 
14) W0C     写0清零，写1时无影响，读取时无影响 
15) W0S     写0设置所有的位，写1时无影响，读取时无影响 
16) W0T     写0入时会翻转，写1时无影响，读取时无影响 
17) W1SRC   写1设置所有的位，写0时无影响，读清零 
18) W1CRS   写1清零，写0时无影响，读设置所有位 
19) W0SRC   写0设置所有的位，写1时无影响，读清零 
20) W0CRS   写0清零，写1时无影响，读设置所有位 
21) WO      尽可能写入，读取时会出错 
22) WOC     写入时清零，读取时出错 
23) WOS     写入时设置所有位，读取时会出错 
24) W1      在复位（reset）后，第一次会尽量写入，其他写入无影响，读取时无影响 
25) WO1     在复位后，第一次会尽量写入，其他的写入无影响，读取时会出错 
'''

# ==========================================================
#  func process excel                              start#{{{
# ==========================================================
def nullUp2Valid(p_sheet, row, col):
    ''' 如果cell(row, col)为空，则用上一个单元格的内容代替 '''
    if p_sheet.cell(row, col).ctype != 0:
        return p_sheet.cell(row, col).value
    else:
        return nullUp2Valid(p_sheet, row-1, col)

def getValueCol(p_sheet, value):
    ''' 返回匹配值出现的第一个列编号 '''
    for row in range (p_sheet.nrows):
        for col in range (p_sheet.ncols):
            if p_sheet.cell(row, col).value == value:
                return col
# ==========================================================
#  func process excel                                end#}}}
# ==========================================================

# ==========================================================
#  func process bit/bus/width                      start#{{{
# ==========================================================
def bit2width(var1): 
    ''' eg. input '[8:7]' return 2, input without ':' return 1 '''
    if ":" in var1:
        var1 = var1.replace('[', '').replace(']', '')
        var1 = var1.split(':')
        var1 = int(var1[0]) - int(var1[1]) + 1
        return var1
    else:
        return 1

def get_bit_low(var1): 
    ''' eg. input '[8:7]' return 7, input without ':' return the digit '''
    return int(var1.replace('[', '').replace(']', '').split(':')[-1])

def get_bit_high(var1): 
    ''' eg. input '[8:7]' return 8, input without ':' return the digit '''
    return int(var1.replace('[', '').replace(']', '').split(':')[0])

def width2bus(var1):
    ''' larger than 1 return '[var1-1:0]', else return '' '''
    if var1 == 1:
        return ''
    else:
        return '[' + str(var1 - 1) + ':0]'

def bus_width(var1):
    var1 = bit2width(var1)
    var1 = width2bus(var1)
    return var1
# ==========================================================
#  func process bit/bus/width                        end#}}}
# ==========================================================

# ==========================================================
#  func process write as is reg                    start#{{{
# ==========================================================
def wr_block(reg, fld, rst, bit):
    wr_str = []
    wr_str.append("    always @(posedge clk or negedge rst_n) begin")
    wr_str.append("        if(!rst_n) begin")
    wr_str.append("            r_%s <= %s'%s;" % (fld, bit2width(bit), rst))
    wr_str.append("        end else if(c_%s_wr) begin" % (reg.lower()))
    wr_str.append("            r_%s <= pwdata%s;" % (fld, bit))
    wr_str.append("        end")
    wr_str.append("    end")
    wr_str.append("")
    return wr_str

def wrc_block(reg, fld, rst, bit):
    wrc_str = []
    wrc_str.append("    always @(posedge clk or negedge rst_n) begin")
    wrc_str.append("        if(!rst_n) begin")
    wrc_str.append("            r_%s <= %s'%s;" % (fld, bit2width(bit), rst))
    wrc_str.append("        end else if(c_%s_wr) begin" % (reg.lower()))
    wrc_str.append("            r_%s <= pwdata%s;" % (fld, bit))
    wrc_str.append("        end else if(c_%s_wrc_clr) begin" % (fld.lower()))
    wrc_str.append("            r_%s <= c_%s_wrc_clr_val;" % (fld, fld))
    wrc_str.append("        end")
    wrc_str.append("    end")
    wrc_str.append("")
    return wrc_str

def wrs_block(reg, fld, rst, bit):
    wrs_str = []
    wrs_str.append("    always @(posedge clk or negedge rst_n) begin")
    wrs_str.append("        if(!rst_n) begin")
    wrs_str.append("            r_%s <= %s'%s;" % (fld, bit2width(bit), rst))
    wrs_str.append("        end else if(c_%s_wr) begin" % (reg.lower()))
    wrs_str.append("            r_%s <= pwdata%s;" % (fld, bit))
    wrs_str.append("        end else if(c_%s_wrs_set) begin" % (fld.lower()))
    wrs_str.append("            r_%s <= c_%s_wrs_set_val;" % (fld, fld))
    wrs_str.append("        end")
    wrs_str.append("    end")
    wrs_str.append("")
    return wrs_str
   
def wo_block(reg, fld, rst, bit):
    wo_str = []
    wo_str.append("    always @(posedge clk or negedge rst_n) begin")
    wo_str.append("        if(!rst_n) begin")
    wo_str.append("            r_%s <= %s'%s;" % (fld, bit2width(bit), rst))
    wo_str.append("        end else if(c_%s_wr) begin" % (reg.lower()))
    wo_str.append("            r_%s <= pwdata%s;" % (fld, bit))
    wo_str.append("        end")
    wo_str.append("    end")
    wo_str.append("")
    return wo_str

def w1_block(reg, fld, rst, bit):
    w1_str = []
    w1_str.append("    always @(posedge clk or negedge rst_n) begin")
    w1_str.append("        if(!rst_n) begin")
    w1_str.append("            r_%s_w1_done <= 1'b0;" % (fld))
    w1_str.append("        end else if(c_%s_wr) begin" % (reg.lower()))
    w1_str.append("            r_%s_w1_done <= 1'b1;" % (fld))
    w1_str.append("        end")
    w1_str.append("    end")
    w1_str.append("    always @(posedge clk or negedge rst_n) begin")
    w1_str.append("        if(!rst_n) begin")
    w1_str.append("            r_%s <= %s'%s;" % (fld, bit2width(bit), rst))
    w1_str.append("        end else if(c_%s_wr && (!r_%s_w1_done)) begin" % (reg.lower(), fld.lower()))
    w1_str.append("            r_%s <= pwdata%s;" % (fld, bit))
    w1_str.append("        end")
    w1_str.append("    end")
    w1_str.append("")
    return w1_str

def wo1_block(reg, fld, rst, bit):
    wo1_str = []
    wo1_str.append("    always @(posedge clk or negedge rst_n) begin")
    wo1_str.append("        if(!rst_n) begin")
    wo1_str.append("            r_%s_wo1_done <= 1'b0;" % (fld))
    wo1_str.append("        end else if(c_%s_wr) begin" % (reg.lower()))
    wo1_str.append("            r_%s_wo1_done <= 1'b1;" % (fld))
    wo1_str.append("        end")
    wo1_str.append("    end")
    wo1_str.append("    always @(posedge clk or negedge rst_n) begin")
    wo1_str.append("        if(!rst_n) begin")
    wo1_str.append("            r_%s <= %s'%s;" % (fld, bit2width(bit), rst))
    wo1_str.append("        end else if(c_%s_wr && (!r_%s_wo1_done)) begin" % (reg.lower(), fld.lower()))
    wo1_str.append("            r_%s <= pwdata%s;" % (fld, bit))
    wo1_str.append("        end")
    wo1_str.append("    end")
    wo1_str.append("")
    return wo1_str
# ==========================================================
#  func process write as is reg                      end#}}}
# ==========================================================

# ==========================================================
#  gen_reg_def                                     start#{{{
# ==========================================================
def gen_reg_def(p_sheet, module, addr_width = 32, data_width = 32):
    reg_col = getValueCol(p_sheet, 'RegName')
    fld_col = getValueCol(p_sheet, 'FieldName')
    rst_col = getValueCol(p_sheet, 'ResetValue')
    bit_col = getValueCol(p_sheet, 'Bits')
    access_col = getValueCol(p_sheet, 'Access')
    adr_col = getValueCol(p_sheet, 'OffsetAddress')

    fo = open("%s_def.v" % module, "w")
    module_upper = module.upper()
    fo.write("`ifndef %s_REG_DEFINE\n" % module_upper)
    fo.write("`define %s_REG_DEFINE\n" % module_upper)
    last_reg_name = ''
    for row in range (p_sheet.nrows)[1:]:
        reg_name  = nullUp2Valid(p_sheet, row, reg_col).lower()
        fld_name  = p_sheet.cell(row, fld_col).value.lower()
        rst_value = p_sheet.cell(row, rst_col).value
        rst_value = re.search('[bodh][a-f0-9]+$', rst_value).group()
        bit       = p_sheet.cell(row, bit_col).value
        fld_type  = nullUp2Valid(p_sheet, row, access_col).upper()
        address = "%d'h%s" % (addr_width, nullUp2Valid(p_sheet, row, adr_col).replace('0x', '').replace('0X', ''))
        reg_name_upper = reg_name.upper()
        fld_name_upper = fld_name.upper()
        if reg_name != last_reg_name:
            fo.write("\n")
            fo.write("`define %-35s %s\n" % (module_upper + "_ADDR_" + reg_name_upper,  address))
        last_reg_name = reg_name
        if fld_name != 'reserved':
            fo.write("`define %-35s %s\n" % (module_upper + "_RANGE_" + fld_name_upper,  bit.replace('[', '').replace(']', '')))
            fo.write("`define %-35s %s\n" % (module_upper + "_RANGE_H_" + fld_name_upper,  get_bit_high(bit)))
            fo.write("`define %-35s %s\n" % (module_upper + "_RANGE_L_" + fld_name_upper,  get_bit_low(bit)))
            fo.write("`define %-35s %s'%s\n" % (module_upper + "_DEFVAL_" + fld_name_upper,  bit2width(bit), rst_value))
    fo.write("\n")
    fo.write("`endif // %s_REG_DEFINE\n" % module_upper)
    fo.close()
    print("Successfully generated %s_def.v" % module)
# ==========================================================
#  gen_reg_def                                       end#}}}
# ==========================================================

# ==========================================================
#  gen_reg_hdl                                     start#{{{
# ==========================================================
def gen_reg_hdl(p_sheet, module, addr_width = 32, data_width = 32):
    reg_col = getValueCol(p_sheet, 'RegName')
    fld_col = getValueCol(p_sheet, 'FieldName')
    rst_col = getValueCol(p_sheet, 'ResetValue')
    bit_col = getValueCol(p_sheet, 'Bits')
    access_col = getValueCol(p_sheet, 'Access')
    adr_col = getValueCol(p_sheet, 'OffsetAddress')

    module_name = "%s_cfg" % module
    fo = open("%s.v" % module_name, "w")
    fo.write("// APB interface \n")
    fo.write("// pready effect 1 cycle after psel & penable is valid immediately \n")
    fo.write("// pslverr is 1 when read WO, WOC, WOS or WO1 reg, or is 0 \n")
    fo.write("`default_nettype none\n")
    fo.write("module %s (" % module_name.upper())
    # apb interface
    fo.write("\n" + 4*" " + " input  wire %-10s clk    " % (""))
    fo.write("\n" + 4*" " + ",input  wire %-10s rst_n  " % (""))
    fo.write("\n" + 4*" " + ",input  wire %-10s pwrite " % (""))
    fo.write("\n" + 4*" " + ",input  wire %-10s psel   " % (""))
    fo.write("\n" + 4*" " + ",input  wire %-10s penable" % (""))
    fo.write("\n" + 4*" " + ",input  wire %-10s paddr  " % (width2bus(addr_width)))
    fo.write("\n" + 4*" " + ",input  wire %-10s pwdata " % (width2bus(data_width)))
    fo.write("\n" + 4*" " + ",output wire %-10s prdata " % (width2bus(data_width)))
    fo.write("\n" + 4*" " + ",output wire %-10s pready " % (""))
    fo.write("\n" + 4*" " + ",output wire %-10s pslverr" % (""))

    as_is_list = ['RW','WRC','WRS','WO','W1','WO1']
    
    w1c_list =['W1C','W1CRS']
    w0c_list =['W0C','W0CRS']
    w1s_list =['W1S','W1SRC']
    w0s_list =['W0S','W0SRC']
    w1t_list =['W1T']
    w0t_list =['W0T']

    wc_list = ['WC','WCRS','WOC']
    ws_list = ['WS','WSRC','WOS']
    rc_list = ['RC','WRC','WSRC','W1SRC','W0SRC']
    rs_list = ['RS','WRS','WCRS','W1CRS','W0CRS']

    wo_list = ['WO', 'WO1', 'WOC', 'WOS']
    
    # other port signals
    for row in range (p_sheet.nrows)[1:]:
        fld_name = p_sheet.cell(row, fld_col).value.lower()
        fld_type = nullUp2Valid(p_sheet, row, access_col).upper()
        bit      = p_sheet.cell(row, bit_col).value
        fld_type_lower = fld_type.lower()
        if fld_name != 'reserved':
            if fld_type in as_is_list:
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s" % (bus_width(bit), fld_name))
            else:
                fo.write("\n" + 4*" " + ",input  wire %-10s i_%s" % (bus_width(bit), fld_name))

            if fld_type in w1c_list:
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_clr"     % (''            , fld_name, fld_type_lower))
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_clr_val" % (bus_width(bit), fld_name, fld_type_lower))
            if fld_type in w0c_list:
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_clr"     % (''            , fld_name, fld_type_lower))
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_clr_val" % (bus_width(bit), fld_name, fld_type_lower))
            if fld_type in w1s_list:
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_set"     % (''            , fld_name, fld_type_lower))
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_set_val" % (bus_width(bit), fld_name, fld_type_lower))
            if fld_type in w0s_list:
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_set"     % (''            , fld_name, fld_type_lower))
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_set_val" % (bus_width(bit), fld_name, fld_type_lower))
            if fld_type in w1t_list:
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_tog"     % (''            , fld_name, fld_type_lower))
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_tog_val" % (bus_width(bit), fld_name, fld_type_lower))
            if fld_type in w0t_list:
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_tog"     % (''            , fld_name, fld_type_lower))
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_tog_val" % (bus_width(bit), fld_name, fld_type_lower))

            if fld_type in wc_list:
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_clr"     % (''            , fld_name, fld_type_lower))
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_clr_val" % (bus_width(bit), fld_name, fld_type_lower))
            if fld_type in rc_list and fld_type != 'WRC':
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_clr"     % (''            , fld_name, fld_type_lower))
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_clr_val" % (bus_width(bit), fld_name, fld_type_lower))
            if fld_type in ws_list:
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_set"     % (''            , fld_name, fld_type_lower))
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_set_val" % (bus_width(bit), fld_name, fld_type_lower))
            if fld_type in rs_list and fld_type != 'WRS':
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_set"     % (''            , fld_name, fld_type_lower))
                fo.write("\n" + 4*" " + ",output wire %-10s o_%s_%s_set_val" % (bus_width(bit), fld_name, fld_type_lower))
    fo.write("\n);\n")

    rd_err = {}
    # declare wire and reg
    for row in range (p_sheet.nrows)[1:]:
        reg_name = nullUp2Valid(p_sheet, row, reg_col).lower()
        fld_name = p_sheet.cell(row,fld_col).value.lower()
        fld_type = nullUp2Valid(p_sheet,row,access_col).upper()
        bit      = p_sheet.cell(row, bit_col).value
        fld_type_lower = fld_type.lower()
        if fld_type in as_is_list:
            fo.write("    reg  %-10s r_%s;\n" % (bus_width(bit), fld_name))
        if fld_type == 'WRC':
            fo.write("    wire %-10s c_%s_%s_clr;\n"     % (''            , fld_name, fld_type_lower))
            fo.write("    wire %-10s c_%s_%s_clr_val;\n" % (bus_width(bit), fld_name, fld_type_lower))
        if fld_type == 'WRS':
            fo.write("    wire %-10s c_%s_%s_set;\n"     % (''            , fld_name, fld_type_lower))
            fo.write("    wire %-10s c_%s_%s_set_val;\n" % (bus_width(bit), fld_name, fld_type_lower))
        if fld_type == 'W1':
            fo.write("    reg  %-10s r_%s_w1_done;\n"    % ('', fld_name))
        if fld_type == 'WO1':
            fo.write("    reg  %-10s r_%s_wo1_done;\n"   % ('', fld_name))
        if p_sheet.cell(row, adr_col).value != '':
            rd_err[reg_name] = 0 # init
    fo.write("    reg  %-10s c_rdata;\n" % (width2bus(data_width)))
    fo.write("    reg  %-10s c_slverr;\n" % (''))

    # apb --> reg_wr and reg_rd
    # certain_reg_wr = paddr hit & reg_wr
    # certain_reg_rd = paddr hit & reg_rd
    for row in range (p_sheet.nrows)[1:]:
        reg_name = nullUp2Valid(p_sheet, row, reg_col).lower()
        fld_type = nullUp2Valid(p_sheet, row, access_col).upper()
        if p_sheet.cell(row,adr_col).value != '':
            fo.write("    wire %-10s c_%s_wr;\n" % ('', reg_name))
            fo.write("    wire %-10s c_%s_rd;\n" % ('', reg_name))
        if fld_type in wo_list:
            rd_err[reg_name] = 1 # error occured when read wo, woc, wos or wo1 reg
    fo.write("    wire %-10s c_sel_and_en;\n" % (''))
    fo.write("    reg  %-10s r_sel_and_en;\n" % (''))
    fo.write("    wire %-10s sel_and_en;\n" % (''))
    fo.write("    wire %-10s c_rd_en;  // may be multiple cycles\n" % (''))
    fo.write("    wire %-10s c_reg_wr; // 1 cycle\n" % (''))
    fo.write("    wire %-10s c_reg_rd; // 1 cycle\n" % (''))
    fo.write("    assign c_sel_and_en = psel & penable;\n")
    fo.write("    assign c_rd_en = c_sel_and_en & (~pwrite);\n")
    fo.write("    always @(posedge clk or negedge rst_n) begin\n")
    fo.write("        if(!rst_n) begin\n")
    fo.write("            r_sel_and_en <= 1'b0;\n")
    fo.write("        end else begin\n")
    fo.write("            r_sel_and_en <= c_sel_and_en;\n")
    fo.write("        end\n")
    fo.write("    end\n")
    fo.write("    assign sel_and_en = c_sel_and_en & ~r_sel_and_en;\n")
    fo.write("    assign c_reg_wr = sel_and_en & pwrite;\n")
    fo.write("    assign c_reg_rd = sel_and_en & (~pwrite);\n")
    for row in range (p_sheet.nrows)[1:]:
        reg_name = nullUp2Valid(p_sheet,row,reg_col).lower()
        address = "%d'h%s" % (addr_width, nullUp2Valid(p_sheet, row, adr_col).replace('0x', '').replace('0X', ''))
        if p_sheet.cell(row, adr_col).value != '':
            fo.write("    assign c_%s_wr = ~|(paddr ^ %s) & c_reg_wr;\n" % (reg_name, address))
            fo.write("    assign c_%s_rd = ~|(paddr ^ %s) & c_reg_rd;\n" % (reg_name, address))
  
    # output for write as is, w1c, w0c, w1s, w0s, w1t, w0t, wc, rc, ws, rs
    for row in range (p_sheet.nrows)[1:]:
        reg_name = nullUp2Valid(p_sheet,row,reg_col).lower()
        fld_name = p_sheet.cell(row,fld_col).value.lower()
        bit      = p_sheet.cell(row,bit_col).value
        fld_type = nullUp2Valid(p_sheet,row,access_col).upper()
        fld_type_lower = fld_type.lower()
        if fld_name != 'reserved':
            if fld_type in as_is_list:
                fo.write("    assign o_%s = r_%s;\n" % (fld_name, fld_name))

            if fld_type in w1c_list:
                fo.write("    assign o_%s_%s_clr = c_%s_wr;\n" % (fld_name, fld_type_lower, reg_name))
                fo.write("    assign o_%s_%s_clr_val = i_%s & (~pwdata%s);\n" %(fld_name, fld_type_lower, fld_name, bit))
            if fld_type in w0c_list:
                fo.write("    assign o_%s_%s_clr = c_%s_wr;\n" % (fld_name, fld_type_lower, reg_name))
                fo.write("    assign o_%s_%s_clr_val = i_%s & pwdata%s;\n" %(fld_name, fld_type_lower, fld_name, bit))
            if fld_type in w1s_list:
                fo.write("    assign o_%s_%s_set = c_%s_wr;\n" % (fld_name, fld_type_lower, reg_name))
                fo.write("    assign o_%s_%s_set_val = i_%s | pwdata%s;\n" %(fld_name, fld_type_lower, fld_name, bit))
            if fld_type in w0s_list:
                fo.write("    assign o_%s_%s_set = c_%s_wr;\n" % (fld_name, fld_type_lower, reg_name))
                fo.write("    assign o_%s_%s_set_val = i_%s | (~pwdata%s);\n" %(fld_name, fld_type_lower, fld_name, bit))
            if fld_type in w1t_list:
                fo.write("    assign o_%s_%s_tog = c_%s_wr;\n" % (fld_name, fld_type_lower, reg_name))
                fo.write("    assign o_%s_%s_tog_val = i_%s ^ pwdata%s;\n" %(fld_name, fld_type_lower, fld_name, bit))
            if fld_type in w0t_list:
                fo.write("    assign o_%s_%s_tog = c_%s_wr;\n" % (fld_name, fld_type_lower, reg_name))
                fo.write("    assign o_%s_%s_tog_val = i_%s ^ (~pwdata%s);\n" %(fld_name, fld_type_lower, fld_name, bit))
            
            if fld_type in wc_list:
                fo.write("    assign o_%s_%s_clr = c_%s_wr;\n" % (fld_name, fld_type_lower, reg_name))
                fo.write("    assign o_%s_%s_clr_val = %s'b0;\n" %(fld_name, fld_type_lower, bit2width(bit)))
            if fld_type in rc_list:
                if fld_type != 'WRC':
                    fo.write("    assign o_%s_%s_clr = c_%s_rd;\n" % (fld_name, fld_type_lower, reg_name))
                    fo.write("    assign o_%s_%s_clr_val = %s'b0;\n" %(fld_name, fld_type_lower, bit2width(bit)))
                else:
                    fo.write("    assign c_%s_%s_clr = c_%s_rd;\n" % (fld_name, fld_type_lower, reg_name))
                    fo.write("    assign c_%s_%s_clr_val = %s'b0;\n" %(fld_name, fld_type_lower, bit2width(bit)))
            if fld_type in ws_list:
                fo.write("    assign o_%s_%s_set = c_%s_wr;\n" % (fld_name, fld_type_lower, reg_name))
                fo.write("    assign o_%s_%s_set_val = ~(%s'h0);\n" %(fld_name, fld_type_lower, bit2width(bit)))
            if fld_type in rs_list:
                if fld_type != 'WRS':
                    fo.write("    assign o_%s_%s_set = c_%s_rd;\n" % (fld_name, fld_type_lower, reg_name))
                    fo.write("    assign o_%s_%s_set_val = ~(%s'h0);\n" %(fld_name, fld_type_lower, bit2width(bit)))
                else:
                    fo.write("    assign c_%s_%s_set = c_%s_rd;\n" % (fld_name, fld_type_lower, reg_name))
                    fo.write("    assign c_%s_%s_set_val = ~(%s'h0);\n" %(fld_name, fld_type_lower, bit2width(bit)))

    # for write as is
    for row in range (p_sheet.nrows)[1:]:
        reg_name  = nullUp2Valid(p_sheet, row, reg_col).lower()
        fld_name  = p_sheet.cell(row, fld_col).value.lower()
        rst_value = p_sheet.cell(row, rst_col).value
        rst_value = re.search('[bodh][a-f0-9]+$', rst_value).group()
        bit       = p_sheet.cell(row, bit_col).value
        fld_type  = nullUp2Valid(p_sheet, row, access_col).upper()
        if fld_name.lower() != 'reserved':
            if fld_type == 'RW': 
                fo.write("\n".join(wr_block(reg_name, fld_name, rst_value, bit)))
            if fld_type == 'WRC': 
                fo.write("\n".join(wrc_block(reg_name, fld_name, rst_value, bit)))
            if fld_type == 'WRS': 
                fo.write("\n".join(wrs_block(reg_name, fld_name, rst_value, bit)))
            if fld_type == 'WO': 
                fo.write("\n".join(wo_block(reg_name, fld_name, rst_value, bit)))
            if fld_type == 'W1': 
                fo.write("\n".join(w1_block(reg_name, fld_name, rst_value, bit)))
            if fld_type == 'WO1': 
                fo.write("\n".join(wo1_block(reg_name, fld_name, rst_value, bit)))

    # for rd 
    fo.write("    always @(*) begin\n")
    fo.write("        c_slverr = 1'b0;\n")
    fo.write("        c_rdata = %s'h0;\n" % (data_width))
    fo.write("        case(paddr)\n")
    last_reg_name = ''
    for row in range (p_sheet.nrows)[1:]:
        reg_name = nullUp2Valid(p_sheet, row, reg_col).lower()
        fld_name  = p_sheet.cell(row, fld_col).value.lower()
        bit       = p_sheet.cell(row, bit_col).value
        fld_type  = nullUp2Valid(p_sheet, row, access_col).upper()
        address = "%d'h%s" % (addr_width, nullUp2Valid(p_sheet, row, adr_col).replace('0x', '').replace('0X', ''))
        if reg_name != last_reg_name:
            if last_reg_name != '': 
                fo.write("    %8send\n" % '')
            fo.write("    %8s%s : begin // %s\n" % ('', address, reg_name.upper()))
            fo.write("        %8sc_slverr = 1'b%d;\n" % ('', rd_err[reg_name]))
        last_reg_name = reg_name
        if fld_name == 'reserved' or fld_type in wo_list:
            fo.write("        %8sc_rdata%s = %s'h0;\n" % ('', bit, bit2width(bit)))
        elif fld_type in as_is_list:
            fo.write("        %8sc_rdata%s = r_%s;\n" % ('', bit, fld_name))
        else:
            fo.write("        %8sc_rdata%s = i_%s;\n" % ('', bit, fld_name))
    fo.write("            end\n")
    fo.write("        endcase\n")
    fo.write("    end\n")

    # output
    fo.write("    assign prdata = c_rd_en ? c_rdata : %d'h0;\n" % data_width)
    fo.write("    assign pready = sel_and_en;\n")
    fo.write("    assign pslverr = c_rd_en ? c_slverr : 1'b0;\n")

    fo.write("endmodule\n")
    fo.write("`default_nettype wire")
    fo.close()
    print("Successfully generated %s.v" % module_name)
# ==========================================================
#  gen_reg_hdl                                       end#}}}
# ==========================================================

# ==========================================================
#  gen_reg_cheader                                 start#{{{
# ==========================================================
def gen_reg_cheader(p_sheet, module, base_addr = 0):

    reg_col = getValueCol(p_sheet, 'RegName')
    fld_col = getValueCol(p_sheet, 'FieldName')
    rst_col = getValueCol(p_sheet, 'ResetValue')
    bit_col = getValueCol(p_sheet, 'Bits')
    access_col = getValueCol(p_sheet, 'Access')
    adr_col = getValueCol(p_sheet, 'OffsetAddress')

    fo = open("%s.h" % module, "w")
    fo.write("#ifndef __TYPE_H__\n")
    fo.write("#define __TYPE_H__\n")
    fo.write("\n")
    fo.write("#define REG32(_register_) (*(volatile unsigned int *)(_register_))\n")
    fo.write("#define REG8(_register_)  (*(volatile unsigned char *)(_register_))\n") 
    fo.write("\n") 
    fo.write("#endif\n") 
    fo.write("\n")
    
    module_upper = module.upper()
    fo.write("/************************** Constant Definitions *****************************/\n")
    fo.write("#ifndef __%s_H__\n" % module_upper)
    fo.write("#define __%s_H__\n" % module_upper)
    fo.write("\n")
    fo.write("#define %-19s 0x%x\n" % (module_upper + '_BASEADDR', base_addr))
    for row in range (p_sheet.nrows)[1:]:
        reg_name = nullUp2Valid(p_sheet, row, reg_col).lower()
        fld_name = p_sheet.cell(row, fld_col).value.lower()
        fld_type = nullUp2Valid(p_sheet, row, access_col)
        address = nullUp2Valid(p_sheet, row, adr_col)
        if p_sheet.cell(row,adr_col).value != '':
            fo.write("#define %-19s (%s_BASEADDR + %s)\n" % (reg_name.upper() + '_ADDR', module_upper, address))
 
    fo.write("\n")
    fo.write("#endif\n")
    fo.close()
    print("Successfully generated %s.h" % module)
# ==========================================================
#  gen_reg_cheader                                   end#}}}
# ==========================================================

# ==========================================================
#  gen_reg_ralf                                    start#{{{
# ==========================================================
def gen_reg_ralf(p_sheet, module, data_width = 32):
    reg_col = getValueCol(p_sheet, 'RegName')
    fld_col = getValueCol(p_sheet, 'FieldName')
    rst_col = getValueCol(p_sheet, 'ResetValue')
    bit_col = getValueCol(p_sheet, 'Bits')
    access_col = getValueCol(p_sheet, 'Access')
    adr_col = getValueCol(p_sheet, 'OffsetAddress')
    rdesc_col = getValueCol(p_sheet, 'RegDesc')
    fdesc_col = getValueCol(p_sheet, 'FieldDesc')

    fo = open("%s.ralf" % module, "w")
    fo.write("# write below command in Makefile\n")
    fo.write("# ralgen:\n")
    fo.write("#     ralgen -full64 -l sv -uvm -t %s_regmodel %s.ralf\n" % (module, module))
    fo.write("# use 'source %s.ralf' in top.ralf\n" % module)
    as_is_list = ['RW','WRC','WRS','WO','W1','WO1']
    last_reg_name = ''
    for row in range (p_sheet.nrows)[1:]:
        reg_name  = nullUp2Valid(p_sheet, row, reg_col).lower()
        fld_name  = p_sheet.cell(row, fld_col).value.lower()
        rst_value = p_sheet.cell(row, rst_col).value
        rst_value = re.search('[bodh][a-f0-9]+$',rst_value).group()
        bit       = p_sheet.cell(row, bit_col).value
        fld_type  = nullUp2Valid(p_sheet, row, access_col).upper()
        fld_type_lower = fld_type.lower()
        reg_desc = nullUp2Valid(p_sheet, row, rdesc_col)
        field_desc = p_sheet.cell(row, fdesc_col).value
        reg_name_upper = "%s_%s" % (module.upper(), reg_name.upper())
        if fld_name != 'reserved':
            if(reg_name != last_reg_name):
                if last_reg_name != '': fo.write("}\n")
                fo.write("\n")
                fo.write("register %s {\n" % (reg_name_upper))
                fo.write("#   ToDo\n")
                fo.write("    bytes %d;\n" % (data_width / 8))
                #fo.write("    doc {\n")
                #fo.write("%s\n" % reg_desc)
                #fo.write("    }\n")
            last_reg_name = reg_name;
            if fld_type in as_is_list:
                fo.write("    field %s (o_%s) @%d {\n" % (fld_name, fld_name, get_bit_low(bit)))
            else:
                fo.write("    field %s (i_%s) @%d {\n" % (fld_name, fld_name, get_bit_low(bit)))
            fo.write("        bits %s;\n" % (bit2width(bit)))
            fo.write("        access %s;\n" % (fld_type_lower))
            fo.write("        reset '%s;\n" % (rst_value))
            #fo.write("        doc {\n")
            #fo.write("%s\n" % field_desc)
            #fo.write("        }\n")
            fo.write("    }\n")
    fo.write("}\n")
    
    fo.write("\n")
    fo.write("block %s_regmodel {\n" % module)
    fo.write("    bytes %s;\n" % (data_width / 8))
    for row in range (p_sheet.nrows)[1:]:
        reg_name  = nullUp2Valid(p_sheet, row, reg_col).lower()
        address   = p_sheet.cell(row, adr_col).value.replace('0x', 'h').replace('0X', 'h')
        reg_name_upper = "%s_%s" % (module.upper(), reg_name.upper())
        if p_sheet.cell(row, adr_col).value != '':
            #fo.write("    register    %-23s %-15s @'%s;\n" % (reg_name_upper, "(" + reg_name + ")", address))
            fo.write("    register    %-23s @'%s;\n" % (reg_name_upper, address))

    fo.write("}\n")
    fo.write("\n")
    fo.close()
    print("Successfully generated %s.ralf" % module)
# ==========================================================
#  gen_reg_ralf                                      end#}}}
# ==========================================================

def main():
    (script_path, script_name) = os.path.split(sys.argv[0]) 
    default_msg = " [default '%(default)s']"
    parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description=__DESCRIPTION__)
    parser.add_argument('file', type=str, help='Specify input xls file')
    parser.add_argument('-t', '--type', dest='type', type=str, default='rtl', help='Specify generated file type, could be "rtl", "ralf", "c", "all"' + default_msg)
    parser.add_argument('--aw', dest='aw', type=int, default=32, help='Specify address width' + default_msg)
    parser.add_argument('--dw', dest='dw', type=int, default=64, help='Specify data width' + default_msg)
    parser.add_argument('-n', '--notrun', dest='notrun', action='store_true', default=False, help='Specify not to run, just list sheet names')
    parser.add_argument('--time', dest='time', action='store_true', default=False, help='Specify to show the run time')
    args = vars(parser.parse_args())
    time_start = time.time()

    if not os.path.exists(args['file']):
        print("[Error]: No such file '%s'" % arg['file'])
        sys.exit(1)
    book = xlrd.open_workbook(args['file'])
    sheets_num = len(book.sheet_names())
    for index in range (sheets_num):
        sheet = book.sheet_by_index(index)
        module = sheet.name.lower()
        if args['notrun']:
            print(module)
        else:
            if args['type'] == 'rtl' or args['type'] == 'all':
                gen_reg_def(sheet, module, args['aw'], args['dw'])
                gen_reg_hdl(sheet, module, args['aw'], args['dw'])
            if args['type'] == 'c' or args['type'] == 'all':
                gen_reg_cheader(sheet, module)
            if args['type'] == 'ralf' or args['type'] == 'all':
                gen_reg_ralf(sheet, module, args['dw'])
            if args['type'] not in ['rtl', 'c', 'ralf', 'all']:
                print("Invalid target '%s' for sheet '%s'" % (args['type'], module))

    if args['time']:
        time_end = time.time()
        print('****** time cost %fs' % (time_end - time_start))

if __name__ == '__main__':
    main()
